<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html>
<head>
<title>Java games Minesweeper</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="Java, Swing, games, tutorial, Minesweeper, programming, GUI, multiplatform">
<meta name="description" content="This part of the Java games tutorial presents Minesweeper game.">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content">


<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>Minesweeper</h1>


<p>
In this part of the Java 2D games tutorial, we will create a Minesweeper game clone. 
</p>

<script type="text/javascript"><!--
google_ad_client = "ca-pub-9706709751191532";
/* LargeSquare */
google_ad_slot = "5070696322";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>

<h2>Minesweeper</h2>

<p>
Minesweeper is a popular board game shipped with many operating systems by default. 
The goal of the game is to sweep all mines from a mine field. If the player
clicks on the cell which contains a mine, the mine detonates and the game is over.
Further a cell can contain a number or it can be blank. The number indicates
how many mines are adjacent to this particular cell. We set a mark on a cell
by right clicking on it. This way we indicate, that we believe, there is a mine. 
</p>

<p>
The following Minesweeper clone is a modified and simplified
version of an Java applet which I found at <a href="http://www.javaside.com">javaside.com</a>.
</p>

<div class="codehead">Board.java</div>
<pre class="code">
package mines;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import java.util.Random;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Board extends JPanel {

    private final int NUM_IMAGES = 13;
    private final int CELL_SIZE = 15;

    private final int COVER_FOR_CELL = 10;
    private final int MARK_FOR_CELL = 10;
    private final int EMPTY_CELL = 0;
    private final int MINE_CELL = 9;
    private final int COVERED_MINE_CELL = MINE_CELL + COVER_FOR_CELL;
    private final int MARKED_MINE_CELL = COVERED_MINE_CELL + MARK_FOR_CELL;

    private final int DRAW_MINE = 9;
    private final int DRAW_COVER = 10;
    private final int DRAW_MARK = 11;
    private final int DRAW_WRONG_MARK = 12;

    private int[] field;
    private boolean inGame;
    private int mines_left;
    private Image[] img;
    private int mines = 40;
    private int rows = 16;
    private int cols = 16;
    private int all_cells;
    private JLabel statusbar;


    public Board(JLabel statusbar) {

        this.statusbar = statusbar;

        img = new Image[NUM_IMAGES];

        for (int i = 0; i < NUM_IMAGES; i++) {
            img[i] =
                    (new ImageIcon(this.getClass().getResource((i)
                        + ".png"))).getImage();
        }

        setDoubleBuffered(true);

        addMouseListener(new MinesAdapter());
        newGame();
    }


    public void newGame() {

        Random random;
        int current_col;

        int i = 0;
        int position = 0;
        int cell = 0;

        random = new Random();
        inGame = true;
        mines_left = mines;

        all_cells = rows * cols;
        field = new int[all_cells];
        
        for (i = 0; i < all_cells; i++)
            field[i] = COVER_FOR_CELL;

        statusbar.setText(Integer.toString(mines_left));


        i = 0;
        while (i < mines) {

            position = (int) (all_cells * random.nextDouble());

            if ((position < all_cells) &amp;&amp;
                (field[position] != COVERED_MINE_CELL)) {


                current_col = position % cols;
                field[position] = COVERED_MINE_CELL;
                i++;

                if (current_col > 0) { 
                    cell = position - 1 - cols;
                    if (cell >= 0)
                        if (field[cell] != COVERED_MINE_CELL)
                            field[cell] += 1;
                    cell = position - 1;
                    if (cell >= 0)
                        if (field[cell] != COVERED_MINE_CELL)
                            field[cell] += 1;

                    cell = position + cols - 1;
                    if (cell < all_cells)
                        if (field[cell] != COVERED_MINE_CELL)
                            field[cell] += 1;
                }

                cell = position - cols;
                if (cell >= 0)
                    if (field[cell] != COVERED_MINE_CELL)
                        field[cell] += 1;
                cell = position + cols;
                if (cell < all_cells)
                    if (field[cell] != COVERED_MINE_CELL)
                        field[cell] += 1;

                if (current_col < (cols - 1)) {
                    cell = position - cols + 1;
                    if (cell >= 0)
                        if (field[cell] != COVERED_MINE_CELL)
                            field[cell] += 1;
                    cell = position + cols + 1;
                    if (cell < all_cells)
                        if (field[cell] != COVERED_MINE_CELL)
                            field[cell] += 1;
                    cell = position + 1;
                    if (cell < all_cells)
                        if (field[cell] != COVERED_MINE_CELL)
                            field[cell] += 1;
                }
            }
        }
    }


    public void find_empty_cells(int j) {

        int current_col = j % cols;
        int cell;

        if (current_col > 0) { 
            cell = j - cols - 1;
            if (cell >= 0)
                if (field[cell] > MINE_CELL) {
                    field[cell] -= COVER_FOR_CELL;
                    if (field[cell] == EMPTY_CELL)
                        find_empty_cells(cell);
                }

            cell = j - 1;
            if (cell >= 0)
                if (field[cell] > MINE_CELL) {
                    field[cell] -= COVER_FOR_CELL;
                    if (field[cell] == EMPTY_CELL)
                        find_empty_cells(cell);
                }

            cell = j + cols - 1;
            if (cell < all_cells)
                if (field[cell] > MINE_CELL) {
                    field[cell] -= COVER_FOR_CELL;
                    if (field[cell] == EMPTY_CELL)
                        find_empty_cells(cell);
                }
        }

        cell = j - cols;
        if (cell >= 0)
            if (field[cell] > MINE_CELL) {
                field[cell] -= COVER_FOR_CELL;
                if (field[cell] == EMPTY_CELL)
                    find_empty_cells(cell);
            }

        cell = j + cols;
        if (cell < all_cells)
            if (field[cell] > MINE_CELL) {
                field[cell] -= COVER_FOR_CELL;
                if (field[cell] == EMPTY_CELL)
                    find_empty_cells(cell);
            }

        if (current_col < (cols - 1)) {
            cell = j - cols + 1;
            if (cell >= 0)
                if (field[cell] > MINE_CELL) {
                    field[cell] -= COVER_FOR_CELL;
                    if (field[cell] == EMPTY_CELL)
                        find_empty_cells(cell);
                }

            cell = j + cols + 1;
            if (cell < all_cells)
                if (field[cell] > MINE_CELL) {
                    field[cell] -= COVER_FOR_CELL;
                    if (field[cell] == EMPTY_CELL)
                        find_empty_cells(cell);
                }

            cell = j + 1;
            if (cell < all_cells)
                if (field[cell] > MINE_CELL) {
                    field[cell] -= COVER_FOR_CELL;
                    if (field[cell] == EMPTY_CELL)
                        find_empty_cells(cell);
                }
        }

    }

    public void paint(Graphics g) {

        int cell = 0;
        int uncover = 0;


        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {

                cell = field[(i * cols) + j];

                if (inGame &amp;&amp; cell == MINE_CELL)
                    inGame = false;

                if (!inGame) {
                    if (cell == COVERED_MINE_CELL) {
                        cell = DRAW_MINE;
                    } else if (cell == MARKED_MINE_CELL) {
                        cell = DRAW_MARK;
                    } else if (cell > COVERED_MINE_CELL) {
                        cell = DRAW_WRONG_MARK;
                    } else if (cell > MINE_CELL) {
                        cell = DRAW_COVER;
                    }


                } else {
                    if (cell > COVERED_MINE_CELL)
                        cell = DRAW_MARK;
                    else if (cell > MINE_CELL) {
                        cell = DRAW_COVER;
                        uncover++;
                    }
                }

                g.drawImage(img[cell], (j * CELL_SIZE),
                    (i * CELL_SIZE), this);
            }
        }


        if (uncover == 0 &amp;&amp; inGame) {
            inGame = false;
            statusbar.setText("Game won");
        } else if (!inGame)
            statusbar.setText("Game lost");
    }


    class MinesAdapter extends MouseAdapter {
        public void mousePressed(MouseEvent e) {

            int x = e.getX();
            int y = e.getY();

            int cCol = x / CELL_SIZE;
            int cRow = y / CELL_SIZE;

            boolean rep = false;


            if (!inGame) {
                newGame();
                repaint();
            }


            if ((x < cols * CELL_SIZE) &amp;&amp; (y < rows * CELL_SIZE)) {

                if (e.getButton() == MouseEvent.BUTTON3) {

                    if (field[(cRow * cols) + cCol] > MINE_CELL) {
                        rep = true;

                        if (field[(cRow * cols) + cCol] <= COVERED_MINE_CELL) {
                            if (mines_left > 0) {
                                field[(cRow * cols) + cCol] += MARK_FOR_CELL;
                                mines_left--;
                                statusbar.setText(Integer.toString(mines_left));
                            } else
                                statusbar.setText("No marks left");
                        } else {

                            field[(cRow * cols) + cCol] -= MARK_FOR_CELL;
                            mines_left++;
                            statusbar.setText(Integer.toString(mines_left));
                        }
                    }

                } else {

                    if (field[(cRow * cols) + cCol] > COVERED_MINE_CELL) {
                        return;
                    }

                    if ((field[(cRow * cols) + cCol] > MINE_CELL) &amp;&amp;
                        (field[(cRow * cols) + cCol] < MARKED_MINE_CELL)) {

                        field[(cRow * cols) + cCol] -= COVER_FOR_CELL;
                        rep = true;

                        if (field[(cRow * cols) + cCol] == MINE_CELL)
                            inGame = false;
                        if (field[(cRow * cols) + cCol] == EMPTY_CELL)
                            find_empty_cells((cRow * cols) + cCol);
                    }
                }

                if (rep)
                    repaint();

            }
        }
    }
}
</pre>


<p>
First we will define the constants used in our game. 
</p>

<pre class="explanation">
private final int NUM_IMAGES = 13;
private final int CELL_SIZE = 15;
</pre>

<p>
There are 13 images used in this game. A cell can be surrounded by maximum of
8 mines, so we need numbers 1..8. We need images for an empty cell, a mine, 
a covered cell, a marked cell and finally for a wrongly marked cell. 
The size of each of the images is 15x15 px.
</p>

<pre class="explanation">
private final int COVER_FOR_CELL = 10;
private final int MARK_FOR_CELL = 10;
private final int EMPTY_CELL = 0;
...
</pre>

<p>
A mine field is an array of numbers. For example 0 denotes an empty cell. 
Number 10 is used for a cell cover as well as for a mark. Using constants
improves readability of the code.
</p>


<pre class="explanation">
private int[] field;
</pre>

<p>
The field is an array of numbers. Each cell in the field 
has a specific number. E.g. a mine cell has number 9. A cell with number 2, meaning
it is adjacent to two mines, has number two. The numbers are added. For example, a covered
mine has number 19, 9 for the mine and 10 for the cell cover etc.
</p>

<pre class="explanation">
private int mines = 40;
private int rows = 16;
private int cols = 16;
</pre>

<p>
The minefield in our game has 40 hidden mines. There are 16 rows and 16 columns in this field.
So there are 256 cells together in the minefield. 
</p>


<pre class="explanation">
for (int i = 0; i < NUM_IMAGES; i++) {
    img[i] =
        (new ImageIcon(this.getClass().getResource((i)
            + ".png"))).getImage();
}
</pre>

<p>
Here we load our images into the image array. The images are named 0.png, 1.png ... 12.png. 
</p>

<p>
The newGame() initiates the Minesweeper game. 
</p>

<pre class="explanation">
all_cells = rows * cols;
field = new int[all_cells];

for (i = 0; i < all_cells; i++)
    field[i] = COVER_FOR_CELL;
</pre>

<p>
These lines set up the mine field. Every cell is covered by default. 
</p>

<pre class="explanation">
while (i < mines) {

    position = (int) (all_cells * random.nextDouble());

    if ((position < all_cells) &amp;&amp;
        (field[position] != COVERED_MINE_CELL)) {
        
        current_col = position % cols;
        field[position] = COVERED_MINE_CELL;
    ...
</pre>

<p>
In the while cycle we randomly position all mines in the field.
</p>

<pre class="explanation">
cell = position - cols;
if (cell >= 0)
    if (field[cell] != COVERED_MINE_CELL)
        field[cell] += 1;
</pre>

<p>
Each of the cells can be surrounded up to 8 cells. (This does not
apply to the border cells.) We raise the number for adjacent cells
for each of the randomly placed mine. In our example, we add 1 to
the top neighbor of the cell in question.
</p>

<p>
In the find_empty_cells() method, we find empty cells. If the player clicks 
on a mine cell, the game is over. If he clicks on a cell adjacent to a mine, 
he uncovers a number indicating how many mines the cell is adjacent to. Clicking
on an empty cell leads to uncovering many other empty cells
plus cells with a number that form a border around a space of 
empty borders. We use a recursive algorithm to find empty cells.
</p>


<pre class="explanation">
cell = j - 1;
if (cell >= 0)
    if (field[cell] > MINE_CELL) {
        field[cell] -= COVER_FOR_CELL;
        if (field[cell] == EMPTY_CELL)
            find_empty_cells(cell);
}
</pre>

<p>
In this code, we check the cell that is left to an empty cell in question. If it is not
empty, uncover it. If it is empty, repeat the whole process by recursively calling the 
find_empty_cells() method.
</p>

<p>
The paint() method turns numbers into images. 
</p>

<pre class="explanation">
if (!inGame) {
    if (cell == COVERED_MINE_CELL) {
        cell = DRAW_MINE;
    } else if (cell == MARKED_MINE_CELL) {
        cell = DRAW_MARK;
    } else if (cell > COVERED_MINE_CELL) {
        cell = DRAW_WRONG_MARK;
    } else if (cell > MINE_CELL) {
        cell = DRAW_COVER;
    }
}
</pre>

<p>
If the game is over and we lost, we show all uncovered mines
if any and show all wrongly marked cells if any.
</p>

<pre class="explanation">
g.drawImage(img[cell], (j * CELL_SIZE),
    (i * CELL_SIZE), this);
</pre>

<p>
This code line draws every cell. 
</p>

<p>
In the mousePressed() method we react to mouse clicks. The Minesweeper game is 
controlled solely by mouse. We react to left and right mouse clicks. 
</p>


<pre class="explanation">
field[(cRow * cols) + cCol] += MARK_FOR_CELL;
mines_left--;
</pre>

<p>
If we right click on an unmarked cell, we add
MARK_FOR_CELL to the number representing the cell. This leads to drawing a covered
cell with a mark in the paint() method.
</p>

<pre class="explanation">
if (field[(cRow * cols) + cCol] > COVERED_MINE_CELL) {
    return;
}
</pre>

<p>
Nothing happens, if we click on the covered &amp; marked cell. 
It must by first uncovered by another right click and only
then it is possible to left click on it. 
</p>

<pre class="explanation">
field[(cRow * cols) + cCol] -= COVER_FOR_CELL;
</pre>

<p>
Left click removes a cover from the cell.
</p>

<pre class="explanation">
if (field[(cRow * cols) + cCol] == MINE_CELL)
    inGame = false;
if (field[(cRow * cols) + cCol] == EMPTY_CELL)
    find_empty_cells((cRow * cols) + cCol);
</pre>

<p>
In case we left clicked on a mine, the game is over. If we left clicked on an empty
cell, we call the find_empty_cells() method, which recursively finds all adjacent empty cells. 
</p>

<div class="codehead">Mines.java</div>
<pre class="code">
package mines;

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JLabel;


public class Mines extends JFrame {

    private final int WIDTH = 250;
    private final int HEIGHT = 290;

    private JLabel statusbar;
    
    public Mines() {

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(WIDTH, HEIGHT);
        setLocationRelativeTo(null);
        setTitle("Minesweeper");

        statusbar = new JLabel("");
        add(statusbar, BorderLayout.SOUTH);

        add(new Board(statusbar));

        setResizable(false);
        setVisible(true);
    }
    
    public static void main(String[] args) {
        new Mines();
    }
}
</pre>

<p>
This is the main class. 
</p>


<img src="/img/gfx/javagames/minesweeper.png" alt="Minesweeper">
<div class="figure">Figure: Minesweeper</div>


<p>
In this part of the Java 2D games tutorial, we created a 
Java clone of the Minesweeper game.
</p>


<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div>
<br>



<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡ 
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified February 16, 2009  <span class="copyright">&copy; 2007 - 2013 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>


